/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.sys>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: data_type_generator.cpp
 *
 * Purpose: data type generator implementation
 *
 * Developer:
 *   wen.gu , 2021-05-10
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/

#include "data_type_generator.h"
#include <map>
#include <sstream>

#include "key_word.h"
#include "file_content.h"
#include "code_generator_utils.h"

#include "log.h"
/******************************************************************************
 **    MACROS
 ******************************************************************************/


/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

/**key: the string of type in sidl, val: the string of type in cpp*/
using DataTypeStrMap = std::map<std::string, std::string>;

/** key: data type define in enum, val: the string of type in cpp */
using DataTypeStrMap2 = std::map<DataType, std::string>;

static const DataTypeStrMap gBaseCppType = 
{
    {KW_TYPE_STRING, BASE_TYPE_STRING},
    {KW_TYPE_BOOL,   BASE_TYPE_BOOL},
    {KW_TYPE_INT8,   BASE_TYPE_INT8},
    {KW_TYPE_INT16,  BASE_TYPE_INT16},
    {KW_TYPE_INT32,  BASE_TYPE_INT32},
    {KW_TYPE_INT64,  BASE_TYPE_INT64},
    {KW_TYPE_UINT8,  BASE_TYPE_UINT8},
    {KW_TYPE_UINT16, BASE_TYPE_UINT16},
    {KW_TYPE_UINT32, BASE_TYPE_UINT32},
    {KW_TYPE_UINT64, BASE_TYPE_UINT64}, 
    {KW_TYPE_FLOAT,  BASE_TYPE_FLOAT},  
    {KW_TYPE_DOUBLE, BASE_TYPE_DOUBLE}  

};

static const DataTypeStrMap2 gDataTypeMap = 
{
    {DataType::kString, BASE_TYPE_STRING},
    {DataType::kBool,   BASE_TYPE_BOOL},
    {DataType::kInt8,   BASE_TYPE_INT8},
    {DataType::kInt16,  BASE_TYPE_INT16},
    {DataType::kInt32,  BASE_TYPE_INT32},
    {DataType::kInt64,  BASE_TYPE_INT64},
    {DataType::kUInt8,  BASE_TYPE_UINT8},
    {DataType::kUInt16, BASE_TYPE_UINT16},
    {DataType::kUInt32, BASE_TYPE_UINT32},
    {DataType::kUInt64, BASE_TYPE_UINT64},
    {DataType::kFloat,  BASE_TYPE_FLOAT},
    {DataType::kDouble, BASE_TYPE_DOUBLE},
    {DataType::kStruct, BASE_TYPE_STRUCT},
    {DataType::kVector, BASE_TYPE_VECTOR},
    {DataType::kEnum,   BASE_TYPE_ENUM},
    {DataType::kMap,    BASE_TYPE_MAP},    
};

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/

static inline const std::string& DataType2Str(const DataTypeStrMap2& dtsm, DataType dt)
{
    DataTypeStrMap2::const_iterator it = dtsm.find(dt);

    if (it != dtsm.end())
    {
        return it->second;
    }

    return "";          
}


static inline const std::string& Str2CppType(const DataTypeStrMap& dtsm, const std::string& str)
{
    DataTypeStrMap::const_iterator it = dtsm.find(str);

    if (it != dtsm.end())
    {
        return it->second;
    }

    return "";        
}

static const std::string& DataType2CppType(DataType type)
{
    return DataType2Str(gDataTypeMap, type);
}

static const std::string& BaseType2CppType(const std::string& type)
{
    return Str2CppType(gBaseCppType, type);
}



static void DumpComment(const DataTypeInfo& dti, std::stringstream& code_st)
{
    if (!dti.comment.empty())
    {
        code_st << "/** " << dti.comment << " */\n";        
    }
}

static std::string DumpStruct(const DataTypeInfo& dti)
{
    std::stringstream code_st;
    const DataTypeElements& elements = dti.elements;
    DumpComment(dti, code_st);
    code_st << "struct " << dti.name << "\n{\n";
    
    for (uint32_t i = 0; i < elements.size(); i++)
    {
        const DataTypeElement& dte = elements[i];
        code_st << "    " << dte.type << " " << dte.name << ";\n";
    }

    code_st << "\n};\n\n";

    return std::move(code_st.str());
}

static std::string DumpVector(const DataTypeInfo& dti)
{
    std::stringstream code_st;
    DumpComment(dti, code_st);
    code_st << "using " << dti.name << " = std::vector<" << dti.element_type << ">;\n";

    return std::move(code_st.str());    
}

static std::string DumpEnum(const DataTypeInfo& dti)
{
    std::stringstream code_st;
    const DataTypeElements& elements = dti.elements;
    DumpComment(dti, code_st);
    code_st << "enum class " << dti.name;
    if (!dti.element_type.empty())
    {
        code_st << ": " << dti.element_type;
    }

    code_st << "\n{\n";
    
    for (uint32_t i = 0; i < elements.size(); i++)
    {
        const DataTypeElement& dte = elements[i];
        code_st << "    " << dte.type << " = " << dte.name << ",\n";
    }

    code_st << "\n};\n\n";

    return std::move(code_st.str());
}

static std::string DumpArray(const DataTypeInfo& dti)
{
    std::stringstream code_st;
    DumpComment(dti, code_st);
    code_st << "using " << dti.name << " = " << dti.element_type << "[ "<< dti.elements[0].type << " ];\n\n";

    return std::move(code_st.str());  
}

static std::string DumpMap(const DataTypeInfo& dti)
{
    std::stringstream code_st;
    const DataTypeElements& elements = dti.elements;
    DumpComment(dti, code_st);
    /**
     *  DataTypeElement::type as the key type of map 
     * DataTypeElement::name as the value type of map
     */
    code_st << "using " << dti.name << " = std::map<" << elements[0].type << ", " << elements[0].name << ">;\n\n";

    return std::move(code_st.str());
}


std::string DumpDataType(const DataTypeArray& dta)
{
    std::stringstream code_st;
    for (uint32_t i = 0; i < dta.size(); i++)
    {
        DataTypeInfo& dti = dta[i];
        
        switch(dti.type)
        {
        case DataType::kStruct: code_st << DumpStruct(dti); break;
        case DataType::kVector: code_st << DumpVector(dti); break;
        case DataType::kEnum: code_st << DumpEnum(dti); break;
        case DataType::kArray: code_st << DumpArray(dti); break;
        case DataType::kMap: code_st << DumpMap(dti); break;
        default:
            LOGW("unknown composite type: %d\n", (int)dti.type);
            break;
        }
    }

    return std::move(code_st.str());
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
DataTypeGenerator::DataTypeGenerator(const std::string& template_str)
    :template_str_(template_str)
{
    /** todo something */
}

DataTypeGenerator::~DataTypeGenerator()
{
    /** todo something */
}


bool DataTypeGenerator::generate(const DataTypeArray& dta, std::string& out_file)
{

}



